#include "mic.h"
#include <wx/textfile.h>
#include <cmath>

 int sampleRate = 8000;
 const int NUMPTS = (int)(8000 * 1);   // 1 seconds -- needs to be length of buffer
 unsigned char waveIn[NUMPTS];   // 'short int' is a 16-bit type; I request 16-bit samples below
                             // for 8-bit capture, you'd use 'unsigned char' or 'BYTE' 8-bit types


micInput::micInput() {

  WCHAR* fault;
   // Specify recording parameters
  WAVEFORMATEX pFormat;
  pFormat.wFormatTag=WAVE_FORMAT_PCM;     // simple, uncompressed format
  pFormat.nChannels=1;                    //  1=mono, 2=stereo
  pFormat.nSamplesPerSec=sampleRate;      // 44100
  pFormat.nAvgBytesPerSec=sampleRate;   // = nSamplesPerSec * n.Channels * wBitsPerSample/8
  pFormat.nBlockAlign=1;                  // = n.Channels * wBitsPerSample/8
  pFormat.wBitsPerSample=8;              //  16 for high quality, 8 for telephone-grade
  pFormat.cbSize=0;


  result = waveInOpen(&hWaveIn, WAVE_MAPPER,&pFormat, 0L, 0L, WAVE_FORMAT_DIRECT);
  if (result){
    waveInGetErrorText(result, fault, 256);
    wxString message = wxString(fault, wxSTRING_MAXLEN);
    wxMessageBox(message, wxT("Failed to open waveform input device."), wxOK|wxICON_INFORMATION);
    return;
  }

  // Set up and prepare header for input
  WaveInHdr.lpData = (LPSTR)waveIn;
  WaveInHdr.dwBufferLength = NUMPTS;
  WaveInHdr.dwBytesRecorded=0;
  WaveInHdr.dwUser = 0L;
  WaveInHdr.dwFlags = 0L;
  WaveInHdr.dwLoops = 0L;
  waveInPrepareHeader(hWaveIn, &WaveInHdr, sizeof(WAVEHDR));

   //Create efficient architecture-dependant fftw plan
  in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * WaveInHdr.dwBufferLength);
  out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * WaveInHdr.dwBufferLength);
  p = fftw_plan_dft_1d(WaveInHdr.dwBufferLength, in, out, FFTW_FORWARD, FFTW_ESTIMATE);

}

micInput::~micInput () {

}


void micInput::startRecording(wxGauge *theGauge, 	wxTextCtrl* txtDebug){

  WCHAR* fault;
  int counter=0;
  int volume = 0;
  //int absPeak, dbVal;
  //int n = WaveInHdr.dwBufferLength*sizeof(char);
  //double cc;
  double absval;
  //double correction = (double)sampleRate /((double)n);
	//double maxEnergy=0;
	//double frequency=0;



  // Insert a wave input buffer
  result = waveInAddBuffer(hWaveIn, &WaveInHdr, sizeof(WAVEHDR));
  if (result) {
    waveInGetErrorText(result, fault, 256);
    wxString message = wxString(fault, wxSTRING_MAXLEN);
    wxMessageBox(message, wxT("Failed to read block from device."), wxOK|wxICON_INFORMATION);
    return;
  }

  // Commence sampling input
  result = waveInStart(hWaveIn);
  if (result){
    waveInGetErrorText(result, fault, 256);
    wxString message = wxString(fault, wxSTRING_MAXLEN);
    wxMessageBox(message, wxT("Failed to start recording."), wxOK|wxICON_INFORMATION);
    return;
  }
  counter=0;
  // Wait until finished recording

  while( !(WaveInHdr.dwFlags & WHDR_DONE) ) {

  }



//***************Microphone gauge
// may be too slow to show all values...  i%4==0 may suffice (maybe even a much higher mod)
  for(unsigned int i=0; i<WaveInHdr.dwBytesRecorded; i++) {
    volume=(byte)(double)WaveInHdr.lpData[i];
    volume=3*(volume-127);
    theGauge->SetValue(volume);
    //int dbVal = 20 * log10( (double) volume / 32768 );
    //txtDebug->AppendText(wxString::Format(_("%d: %d\n"), WaveInHdr.lpData[counter], volume));
  }
//*********************************



//  txtDebug->Clear();
//  txtDebug->AppendText(_("Frequencies:\n"));

  wxTextFile outFile(_("out1.txt"));
  if(outFile.Exists())
    outFile.Open();
  else
    outFile.Create();
  outFile.Clear();
  for(unsigned int i=0; i<WaveInHdr.dwBytesRecorded; i ++) {
    short int temp = (byte)(double)WaveInHdr.lpData[i];
    //temp = temp << 8;
    //temp += (byte)(double)WaveInHdr.lpData[i+1];
    outFile.AddLine(wxString::Format(_("%d"), temp));
    in[i][0]=temp;
    in[i][1]=0.0;
  }
  outFile.Write();
  outFile.Close();

  fftw_execute(p);

  wxTextFile outFile2(_("out2.txt"));
  if(outFile2.Exists())
    outFile2.Open();
  else
    outFile2.Create();
  outFile2.Clear();
	float frequencies[4000];
	for (unsigned int i = 0; i < 2000; i++) {
		absval = sqrt(out[i][0] * out[i][0]+out[i][1]*out[i][1]);
		frequencies[i*2] = frequencies[i*2+1] = absval;
    outFile2.AddLine(wxString::Format(_("%f"), absval));
    outFile2.AddLine(wxString::Format(_("%f"), absval));
	}
	outFile2.Write();
  outFile2.Close();

//  txtDebug->AppendText(wxString::Format(_("Energy at high A: %f\n"), frequencies[440]));
//  if(frequencies[440] > 1000) {
//    txtDebug->AppendText(_("This was a high A\n"));
//  } else {
//    txtDebug->AppendText(_("This was NOT a high A\n"));
//  }
//  if(frequencies[82] > 1000) {
//    txtDebug->AppendText(_("This was a low E\n"));
//  } else {
//    txtDebug->AppendText(_("This was NOT a low E\n"));
//  }
	//txtDebug->AppendText(wxString::Format(_("Frequency=%d\n"), frequency));
	//txtDebug->AppendText(wxString::Format(_("Most Energy=%f\n"), maxEnergy));

//  outFile.close();

  //playBack();

  //close mic
  waveInUnprepareHeader(hWaveIn, &WaveInHdr, sizeof(WAVEHDR));
  waveInClose(hWaveIn);
}



void micInput::stopRecording(){
  waveInStop(hWaveIn);
}

void micInput::kill() {
  fftw_destroy_plan(p);
  fftw_free(in);
  fftw_free(out);
}

bool micInput::playBack(){
	//Handle for the WAVE device.
	HWAVEOUT hWaveOut;
	WAVEFORMATEX PCMWaveFmtRecord;
	int errorCode;

	PCMWaveFmtRecord.wFormatTag = WAVE_FORMAT_PCM;
	PCMWaveFmtRecord.nChannels = 1;
	PCMWaveFmtRecord.nSamplesPerSec = sampleRate;
	PCMWaveFmtRecord.nAvgBytesPerSec = sampleRate;
	PCMWaveFmtRecord.nBlockAlign = 1;
	PCMWaveFmtRecord.wBitsPerSample = 8;
	errorCode = waveOutOpen(NULL, WAVE_MAPPER,
							 &PCMWaveFmtRecord,
							 0L, 0L, WAVE_FORMAT_QUERY);

	if (errorCode)
	{
		//MessageBox(" Device Cannot handle this format");
		wxMessageBox(wxT("Device Cannot handle this format"), wxT("Blast"), wxOK|wxICON_INFORMATION);
		return false;
	}

	waveOutOpen(&hWaveOut, WAVE_MAPPER,
		&PCMWaveFmtRecord,
		0L, 0L, 0L);

	//Prepare the header.
//	WaveInHdr.lpData = (LPSTR) buffer;
//	WaveInHdr.dwBufferLength = numrcv;
//	WaveInHdr.dwFlags = WaveInHdr.reserved = 0;
//	WaveInHdr.dwLoops = 0;
//	WaveInHdr.lpNext = NULL;

	waveOutPrepareHeader(hWaveOut, &WaveInHdr, sizeof(WaveInHdr));

	//Write data to WAVE device.
	waveOutWrite(hWaveOut, &WaveInHdr, sizeof(WaveInHdr));
	//Loop until playback is finished.
	do {


  } while (!(WaveInHdr.dwFlags & WHDR_DONE)); // inefficient busy wait!

	//Unprepare the wave output header.
	waveOutUnprepareHeader(hWaveOut, &WaveInHdr, sizeof(WaveInHdr));

	//Close the WAVE device.
	waveOutClose(hWaveOut);

	return true;
}

double indexToFrequency(unsigned int p_nBaseFreq,
    unsigned int p_nSamples, unsigned int p_nIndex)
{

    if(p_nIndex >= p_nSamples) {
        return 0.0;
    } else if(p_nIndex <= p_nSamples/2) {
        return ( (double)p_nIndex /
                 (double)p_nSamples * p_nBaseFreq );
    } else {
        return ( -(double)(p_nSamples-p_nIndex) /
                  (double)p_nSamples * p_nBaseFreq );
    }
}
